package copyengine.ai.pathFinder.astart
{
	import copyengine.ai.pathFinder.CEPathFinderConfig;
	import copyengine.ai.pathFinder.astart.node.CEAStartNodeBasic;
	import copyengine.ai.pathFinder.astart.parentTile.CEAStartSearchParentNode;
	import copyengine.utils.Random;
	import copyengine.utils.debug.CELog;

	import flash.utils.Dictionary;

	public final class CEAStartPathFinder
	{
		private var aStartNodeDic:Dictionary;

		private var openNodeList:Vector.<CEAStartSearchParentNode>;

		/**
		 *起始节点
		 */
		private var startNode:CEAStartSearchParentNode;

		/**
		 *当前检察的节点
		 */
		private var currentCheckNode:CEAStartSearchParentNode;

		/**
		 *搜索结束节点
		 */
		private var targetNode:CEAStartSearchParentNode;

		private var searchStartNodeTileIndexX:int;
		private var searchStartNodeTileIndexY:int;

		/**
		 *用于计算启发成本
		 */
		private var heuristicCalculator:INodeHeuristicCostCalculator;

		/**
		 *计算某个Tile是否被Block掉
		 * @see  IAStartBlockNodeManger类定义
		 */
		private var aStartBlockNodeManger:IAStartBlockNodeManger;

		public function CEAStartPathFinder()
		{
		}

		/**
		 * Step1 首先Call AStart的Init函数
		 */
		public function initialize(_heuristicCalculator:INodeHeuristicCostCalculator,
								   _blockNodeManger:IAStartBlockNodeManger):void
		{
			heuristicCalculator = _heuristicCalculator;
			aStartBlockNodeManger = _blockNodeManger;
			openNodeList = new Vector.<CEAStartSearchParentNode>();
		}

		/**
		 * 设置搜索的Dic,当地图数据发生改变后重新设置
		 */
		public function setAStartNodeDic(_aStartNodeDic:Dictionary):void
		{
			aStartNodeDic = _aStartNodeDic;
			//重置所有节点
			for each (var parentTile:CEAStartSearchParentNode in aStartNodeDic)
			{
				parentTile.resetParentNodeAStartProperty();
			}
		}

		/**
		 * 重置搜索数据 (当完成一次搜索后重置当前搜索数据)
		 */
		public function resetSearchParameter(_fromTileIndexX:int, _fromTileIndexY:int, _toTileIndexX:int, _toTileIndexY:int):void
		{
			searchStartNodeTileIndexX = _fromTileIndexX;
			searchStartNodeTileIndexY = _fromTileIndexY;

			openNodeList.length = 0;
			targetNode = getParentTileByIndex(_toTileIndexX, _toTileIndexY);
			currentCheckNode = getParentTileByIndex(_fromTileIndexX, _fromTileIndexY);

			//如果无法从当前的TileDic上面找到起始点或者结束点则当前搜索请求无效
			if (targetNode != null && currentCheckNode != null)
			{
				//重置所有节点
				for each (var parentTile:CEAStartSearchParentNode in aStartNodeDic)
				{
					parentTile.resetParentNodeAStartProperty();
				}
			}
			//			CELog.log("\n\n");
			//			CELog.log("===========================");
			//			CELog.log("Start AStart Search " +
			//					  " Target( " + _toTileIndexX + " , " + _toTileIndexY + " ) Source ( " + _fromTileIndexX + " , " + _fromTileIndexY + " )");
		}

		public function dispose():void
		{
			openNodeList = null;
			startNode = null;
			currentCheckNode = null;
			targetNode = null;
			for each (var parentTile:CEAStartSearchParentNode in aStartNodeDic)
			{
				parentTile.dispose();
			}
			aStartNodeDic = null;
		}

		/**
		 * 在搜索结束后调用,用于回溯AStart路径
		 */
		public function getPathEndNode():CEAStartSearchParentNode
		{
			return targetNode;
		}

		/**
		 *调用搜索函数搜索指定步长,将搜索结果返回
		 */
		public function tickSearch():int
		{
			for (var step:int = 0; step < CEAStartPathFinderConfig.EACH_SEARCH_STEP; step++)
			{
//				CELog.log("========= Search Step : " + step + " =========");
				//如果当前搜索结果是目标则返回
				if (currentCheckNode == null || targetNode == null)
				{
					//当前搜素无效,返回Failed
					return CEPathFinderConfig.SEARCH_RESULT_FAILED;
				}

				if (currentCheckNode == targetNode)
				{
//					CELog.log("======Find Target=====");
					return CEPathFinderConfig.SEARCH_RESULT_SUCCEED;
				}
				else
				{
//					CELog.log("Start to Check ParentTile : " + currentCheckNode.toString());
					var subNodeVector:Vector.<CEAStartNodeBasic> = currentCheckNode.getAllSubNode();
					var subNodeVectorLength:int = subNodeVector.length
					//如果当前节点的子节点超过三个(K3一般会有，相当于还有两个以上的特殊节点)
					//则打乱数组的顺序,这样AStart时候会随机使用特殊节点
					if (subNodeVectorLength >= 3)
					{
						Random.shuffle(subNode);
					}

					//将当前节点Push到Close列表
					//在检查子Node之前先将节点Push到Close列表中
					//比如有Parent节点PA,PB,PC,Elevator EA 引用关系则会为 PA->EA,PB->EA,PC->EA || EA->(PA,PB,PC)
					//当PA通过EA找到PB,PB遍历PB下的EA又会将PA重新Push到OpenList中,所以首先Close掉当前Check的节点
					//这样就不会重复Push
					currentCheckNode.isCloseNode = true;
//					CELog.log("Put CheckParentNode : " + currentCheckNode.toString() + "  to Close Node");

					for each (var subNode:CEAStartNodeBasic in subNodeVector)
					{
//						CELog.log("------>Check SubNode : " + subNode.toString());
						var neighborsTileVector:Vector.<CEAStartSearchParentNode> = subNode.getNeighborsTileVector();
						for each (var neighborParentTile:CEAStartSearchParentNode in neighborsTileVector)
						{
//							CELog.log("--------->Check SubNodeLinkParentIndex" + neighborParentTile.toString());
							//当前节点已经Close
							//如果当前neighbor节点在Close列表中,表明当前neighbor已经被搜索过
							if (isTileClose(neighborParentTile))
							{
//								CELog.log("---------> SubNodeLinkParent" + neighborParentTile.toString() + " is Close Ignore");
								continue;
							}

							//从开始节点移动到该点的步长成本(步长成本均为1即可)
							var costFromStart:int = currentCheckNode.costFromStart + 1;
							//总成本 = 步长成本+启发成本+移动成本
							var totalCost:int = costFromStart + getTileHeuristicCost(neighborParentTile) + subNode.getMoveCost();
//							CELog.log("---------> TotalMoveCost : " + totalCost);

							//如果当前neighbor节点不在open列表中,设置属性后push到open列表
							if (!isTileOpen(neighborParentTile))
							{
//								CELog.log("---------> SubNodeLinkParent" + neighborParentTile.toString() + " is New Link To SercerQueue");
								neighborParentTile.isOpenNode = true;
								openNodeList.push(neighborParentTile);

								neighborParentTile.costFromStart = costFromStart;
								neighborParentTile.totalCost = totalCost;
								neighborParentTile.parentNode = currentCheckNode;
								neighborParentTile.nodeLinkUUID = subNode.getUUID();
							}
							else
							{
								//如果当前neighbor节点在open列表中,但原总成本大于现在计算的总成本则重新赋值
								if (totalCost < neighborParentTile.totalCost)
								{
//									CELog.log("---------> SubNodeLinkParent" + neighborParentTile.toString() + " is CostLow ReLink To SercerQueue");
									neighborParentTile.costFromStart = costFromStart;
									neighborParentTile.totalCost = totalCost;
									neighborParentTile.parentNode = currentCheckNode;
									neighborParentTile.nodeLinkUUID = subNode.getUUID();
								}
							}
						}
					}

					if (openNodeList.length == 0)
					{
						//如果当前没有open的节点则无法达到目标
						return CEPathFinderConfig.SEARCH_RESULT_FAILED;
					}
					else
					{
						openNodeList.sort(openListNodeSortFunction);
						currentCheckNode = openNodeList.pop();
					}
				}
			}
			//限定的所有步长中没有找到指定节点,open列表中仍旧有节点,返回
			return CEPathFinderConfig.SEARCH_RESULT_NOT_FINISHED;
		}

		private function isTileClose(_parentTile:CEAStartSearchParentNode):Boolean
		{
//			trace(aStartBlockNodeManger && aStartBlockNodeManger.isTileBlock(_parentTile.tileIndexX, _parentTile.tileIndexY));
//			return _parentTile.isCloseNode;
			return _parentTile.isCloseNode || (aStartBlockNodeManger && aStartBlockNodeManger.isTileBlock(_parentTile.tileIndexX, _parentTile.tileIndexY));
		}

		private function isTileOpen(_parentTile:CEAStartSearchParentNode):Boolean
		{
			return _parentTile.isOpenNode;
		}

		private function getTileHeuristicCost(_parentTile:CEAStartSearchParentNode):int
		{
			if (_parentTile.heuristicCost == int.MIN_VALUE)
			{
				_parentTile.heuristicCost = heuristicCalculator.getNodeHeuristicCost(_parentTile.getIndexX(), _parentTile.getIndexY(),
																					 targetNode.getIndexX(), targetNode.getIndexY(),
																					 searchStartNodeTileIndexX, searchStartNodeTileIndexY);
			}
			return _parentTile.heuristicCost;
		}

		private function openListNodeSortFunction(_nodeA:CEAStartSearchParentNode, _nodeB:CEAStartSearchParentNode):int
		{
			if (_nodeA.totalCost < _nodeB.totalCost)
			{
				return 1;
			}
			else
			{
				return -1;
			}
		}

		private function getParentTileByIndex(_indexX:int, _indexY:int):CEAStartSearchParentNode
		{
			var key:String = _indexX + "-" + _indexY;
			return aStartNodeDic[key];
		}

	}
}
